package com.elasticcloudservice.predict;


import com.filetool.main.Main;
import com.filetool.util.FileUtil;

import java.text.ParseException;
import java.util.*;

/**
 * @Author: Chy
 * @Description: 遗传算法装箱
 * @Date: Created at 15:44 2018/4/12
 */
public class Allocate_GA {
    public static String[] allocate(String[] inputContent, String[] predict) throws ParseException {

        //获取输入文件中的虚拟机信息
        Map<String, VirtualMachine> vmMap = getVmMapFromInput(inputContent);

        //获取输入文件种所有服务器信息
        List<ElasticCloudServer> allEcs = getEcsListFromInput(inputContent);

        //使用的服务器集合
        List<ElasticCloudServer> ecsList = new ArrayList<>();

        //将预测结果解析成虚拟机的集合
        List<Map<String, VirtualMachine>> predictVmList = getVmListFromPredict(predict, vmMap);

        System.out.println("-------------------" + Main.i + "号用例-----------------" );//提交时把代码注释掉
        //将预测出的虚拟机装入服务器
        Chromosome bestSolution = allocateByGA(predictVmList, vmMap, ecsList, predict, allEcs);
        ecsList = bestSolution.getEcsList();
        int totalVmCPU = 0;
        int totalVmMems = 0;
        int totalVmNum = 0;
        for (ElasticCloudServer ecs : ecsList) {
            for (VirtualMachine vm : ecs.getVirtualMachines()) {
                totalVmCPU += vm.getCPUs();
                totalVmMems += vm.getMems();
                totalVmNum++;
            }
        }
        //微调大法
//        Collections.sort(predictVmList, new VmComparator(target)); //对sortedPredictVmList进行排序
//        int[] totalVmInfo = adjustDF(ecsList, vmMap, predictVmList, predict, totalVmNum, totalVmCPU, totalVmMems);

        System.out.println(ecsList.size());
        int[] totalVmInfo = {totalVmCPU, totalVmMems, totalVmNum};

        //提交时把以下代码注释掉
        System.out.println("服务器数量：" + ecsList.size());
        double useRate = calUseRate(ecsList, totalVmInfo, allEcs);
        System.out.println("使用率：" + useRate);
        String[] testData = FileUtil.read("files/testData/Testcase" + Main.i + ".txt", null); //测试文件
        double predictAccuracy = calPredictAccuracyRate(vmMap, predict, testData);
        System.out.println("预测准确率：" + predictAccuracy);
        System.out.println("得分：" + predictAccuracy * useRate * 100);
        //输出结果
        return getResult(ecsList, totalVmInfo, predict, allEcs);
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 遗传算法 + FF实现装箱
     * @Date: 15:49 2018/4/12
     */
    private static Chromosome allocateByGA(List<Map<String, VirtualMachine>> predictVmList, Map<String, VirtualMachine> vmMap, List<ElasticCloudServer> ecsList, String[] predict, List<ElasticCloudServer> allEcs) {

        final int popSize = 100;  //种群规模
        final int dominants = 5;  //优势个体数量
        final double domRate = 0.95;
        final double mutationRate = 0.1;  //变异率
        final int iteratorTimes = 50;  //迭代次数

        //获取待装箱元素集合
        final List<VirtualMachine> vmList = new ArrayList<>();
        int i = 0;
        for (Map<String, VirtualMachine> map : predictVmList) {
            VirtualMachine virtualMachine = new VirtualMachine();
            for (String key : map.keySet()) {
                virtualMachine.setId(++i);
                virtualMachine.setName(key);
                virtualMachine.setCPUs(map.get(key).getCPUs());
                virtualMachine.setMems(map.get(key).getMems() / 1024);
            }
            vmList.add(virtualMachine);
        }

        //生成初始种群
        Generation generation = new Generation(popSize, dominants, domRate, mutationRate);
        generation.init(popSize, vmList, allEcs);
        //计算种群中每个个体的适应度
        generation.countFitness(generation.getPopulation());
        //根据适应度对种群排序
        generation.sort();
        Chromosome bestSolution = generation.getBestChromosome();
        int k = 0;
        while (k++ < iteratorTimes) {

            //交叉变异以产生下一代
            generation = generation.nextGeneration(popSize, dominants, domRate, mutationRate, allEcs);
            generation.sort();
            if (generation.getBestChromosome().getFitness() < bestSolution.getFitness())
                bestSolution = generation.getBestChromosome();
        }

        return bestSolution;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 获取输入文件中所有服务器的信息
     * @Date: 19:27 2018/4/20
     */
    private static List<ElasticCloudServer> getEcsListFromInput(String[] inputContent) {
        List<ElasticCloudServer> ecsList = new ArrayList<>();

        int ecsTypes = Integer.parseInt(inputContent[0]);  //服务器种类数

        for (int i = 1; i < 1 + ecsTypes; i++) {
            String[] splitArray = inputContent[i].split(" ");
            String ecsName = splitArray[0];
            int ecsCPU = Integer.parseInt(splitArray[1]);
            int ecsMem = Integer.parseInt(splitArray[2]);
            ElasticCloudServer ecs = new ElasticCloudServer(ecsName, ecsCPU, ecsMem);
            ecsList.add(ecs);
        }

        return ecsList;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 微调预测结果，面向分数编程
     * @Date: 23:30 2018/4/7
     */
    private static int[] adjustDF(List<ElasticCloudServer> bestEcsList, Map<String, VirtualMachine> vmMap, List<Map<String, VirtualMachine>> predictVmList, String[] predict, int totalVmNum, int totalVmCPU, int totalVmMems) {
        int k = 0;
        Iterator iterator = bestEcsList.iterator();
        while (iterator.hasNext()) {
            k++;
            ElasticCloudServer ecs = (ElasticCloudServer) iterator.next();
            if (ecs.getVmList().size() <= 2) {
                System.out.println("服务器" + k + "只装了少于2台的虚拟机");
                for (String name : ecs.getVmList()) {
                    for (int i = 0; i < predict.length; i++) {
                        String[] split = predict[i].split(" ");
                        if (split[0].equals(name)) {
                            predict[i] = name + " " + (Integer.parseInt(split[1]) - 1);
                            totalVmNum--;
                            totalVmCPU -= vmMap.get(name).getCPUs();
                            totalVmMems -= vmMap.get(name).getMems() / 1024;
                        }
                    }
                }
                iterator.remove();
            } else {
                for (Map<String, VirtualMachine> map : predictVmList) {
                    String vmName = null;
                    for (String key : map.keySet()) {
                        vmName = key;
                    }
                    int vmCPU = vmMap.get(vmName).getCPUs();
                    int vmMems = vmMap.get(vmName).getMems() / 1024; //MB转换为GB

                    if (ecs.getCPUs() >= 2 * vmCPU && ecs.getMems() >= 2 * vmMems) {
                        System.out.println("服务器" + k + "还能装下2台" + vmName);
                        ecs.getVmList().add(vmName);  //把最大的虚拟机装进服务器
                        ecs.getVmList().add(vmName);
                        ecs.setCPUs(ecs.getCPUs() - 2 * vmCPU); //服务器资源相应减少
                        ecs.setMems(ecs.getMems() - 2 * vmMems);
                        totalVmCPU += vmCPU * 2;  //虚拟机总资源相应增加
                        totalVmMems += vmMems * 2;
                        for (int i = 0; i < predict.length; i++) {
                            String[] split = predict[i].split(" ");
                            if (split[0].equals(vmName)) {
                                predict[i] = vmName + " " + (Integer.parseInt(split[1]) + 2);
                                totalVmNum += 2;
                            }
                        }
                    }

//                    while (ecs.getCPUs() >= vmCPU && ecs.getMems() >= vmMems) {
//                        System.out.println("服务器" + k + "还能装下" + vmName);
//                        ecs.getVmList().add(vmName);  //把最大的虚拟机装进服务器
//                        ecs.setCPUs(ecs.getCPUs() - vmCPU); //服务器资源相应减少
//                        ecs.setMems(ecs.getMems() - vmMems); //服务器资源相应减少
//                        for (int i = 0; i < predict.length; i++) {
//                            String[] split = predict[i].split(" ");
//                            if (split[0].equals(vmName)) {
//                                predict[i] = vmName + " " + (Integer.parseInt(split[1]) + 1);
//                                totalVmNum += 1;
//                                totalVmCPU += vmCPU;  //虚拟机总资源相应增加
//                                totalVmMems += vmMems;
//                            }
//                        }
//                    }
                }
            }
        }
        return new int[]{totalVmCPU, totalVmMems, totalVmNum};
    }


    /**
     * @Author: Chy
     * @Param:
     * @Description: 获取虚拟机的种类，并存入Map集合
     * @Date: 13:55 2018/3/10
     */
    private static Map<String, VirtualMachine> getVmMapFromInput(String[] inputContent) {
        Map<String, VirtualMachine> vmMap = new HashMap<>();
        int ecsTypes = Integer.parseInt(inputContent[0]);  //服务器种类数
        int flavorTypes = Integer.parseInt(inputContent[2 + ecsTypes]);   //虚拟机种类数

        for (int i = 3 + ecsTypes; i < 3 + ecsTypes + flavorTypes; i++) {
            String[] splitArray = inputContent[i].split(" ");
            VirtualMachine vm = new VirtualMachine();
            vm.setName(splitArray[0]);
            vm.setCPUs(Integer.parseInt(splitArray[1]));
            vm.setMems(Integer.parseInt(splitArray[2]));
            vmMap.put(splitArray[0], vm);
        }

        return vmMap;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 将预测结果解析成虚拟机集合
     * @Date: 18:12 2018/3/10
     */
    private static List<Map<String, VirtualMachine>> getVmListFromPredict(String[] predict, Map<String, VirtualMachine> vmMap) {

        //将预测结果解析到一个List<Map<String,VirtualMachine>>中
        List<Map<String, VirtualMachine>> sortedPredictVmList = new ArrayList<>();
        for (String predictEntryStr : predict) {
            String[] split = predictEntryStr.split(" ");
            for (int i = 0; i < Integer.parseInt(split[1]); i++) {
                Map<String, VirtualMachine> predictEntryMap = new HashMap<>();
                predictEntryMap.put(split[0], vmMap.get(split[0]));
                sortedPredictVmList.add(predictEntryMap);
            }
        }
        //对sortedPredictVmList进行排序
//        Collections.sort(sortedPredictVmList, new VmComparator(target));

        return sortedPredictVmList;
    }


    /**
     * @Author: Chy
     * @Param:
     * @Description: 输出最终结果
     * @Date: 11:59 2018/3/10
     */
    public static String[] getResult(List<ElasticCloudServer> ecsList, int[] totalVmInfo, String[] predictResult, List<ElasticCloudServer> allEcs) {
        int ecsNum = ecsList.size();  //服务器数量
        int generalNum = 0, largeMemoryNum = 0, highPerformanceNum = 0;
        String general = "General";
        String largeMemory = "Large-Memory";
        String highPerformance = "High-Performance";
        List<ElasticCloudServer> generalServer = new ArrayList<>();
        List<ElasticCloudServer> largeMemoryServer = new ArrayList<>();
        List<ElasticCloudServer> highPerformanceServer = new ArrayList<>();

        List<String> resultList = new ArrayList<>();
        resultList.add(totalVmInfo[2] + "");  //虚拟机总数量
        //各种虚拟机的数量
        for (int i = 0; i < predictResult.length; i++) {
            resultList.add(predictResult[i]);
        }
        resultList.add("");  //虚拟机数量后的空行

        //统计各类型的服务器数量
        for (ElasticCloudServer ecs : ecsList){
            if (ecs.getName().equals(general)) {
                generalNum++;
                generalServer.add(ecs);
            }
            else if (ecs.getName().equals(largeMemory)){
                largeMemoryNum++;
                largeMemoryServer.add(ecs);
            }
            else if (ecs.getName().equals(highPerformance)) {
                highPerformanceNum++;
                highPerformanceServer.add(ecs);
            }
        }

        //获取general类型的服务器里的虚拟机信息
        if (generalNum != 0){
            resultList.add(general + " " + generalNum);
            int i = 1;
            for (ElasticCloudServer ecs : generalServer){
                List<String> vmList = ecs.getVmList();
                //统计每个服务器内放置的虚拟机信息
                Set<String> vmSet = new HashSet<>(vmList);
                StringBuffer sb = new StringBuffer();
                sb.append(general + "-" + i + " ");
                for (String vmName : vmSet) {
                    sb.append(vmName + " " + Collections.frequency(vmList, vmName) + " ");
                }
                resultList.add(sb.toString().trim());
                i++;
            }
        }

        //获取Large-Memory类型的服务器里的虚拟机信息
        if (largeMemoryNum != 0){
            resultList.add("");
            resultList.add(largeMemory + " " + largeMemoryNum);
            int i = 1;
            for (ElasticCloudServer ecs : largeMemoryServer){
                List<String> vmList = ecs.getVmList();
                //统计每个服务器内放置的虚拟机信息
                Set<String> vmSet = new HashSet<>(vmList);
                StringBuffer sb = new StringBuffer();
                sb.append(largeMemory + "-" + i + " ");
                for (String vmName : vmSet) {
                    sb.append(vmName + " " + Collections.frequency(vmList, vmName) + " ");
                }
                resultList.add(sb.toString().trim());
                i++;
            }
        }

        //获取High-Performance类型的服务器里的虚拟机信息
        if (highPerformanceNum != 0){
            resultList.add("");
            resultList.add(highPerformance + " " + highPerformanceNum);
            int i = 1;
            for (ElasticCloudServer ecs : highPerformanceServer){
                List<String> vmList = ecs.getVmList();
                //统计每个服务器内放置的虚拟机信息
                Set<String> vmSet = new HashSet<>(vmList);
                StringBuffer sb = new StringBuffer();
                sb.append(highPerformance + "-" + i + " ");
                for (String vmName : vmSet) {
                    sb.append(vmName + " " + Collections.frequency(vmList, vmName) + " ");
                }
                resultList.add(sb.toString().trim());
                i++;
            }
        }

        String[] result = new String[resultList.size()];
        int i = 0;
        for (String str : resultList)
            result[i++] = str;
        return result;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 自定义的服务器比较器
     * @Date: 17:41 2018/3/23
     */
    private static class EcsComparator implements Comparator<ElasticCloudServer> {

        private String target;

        public EcsComparator(String target) {
            this.target = target;
        }

        @Override
        public int compare(ElasticCloudServer ecs1, ElasticCloudServer ecs2) {

            //若优化目标是CPU，则以CPU核心数进行排序
            if (target.equals("CPU")) {
                if (ecs1.getCPUs() > ecs2.getCPUs()) {
                    return 1;
                } else if (ecs1.getCPUs() < ecs2.getCPUs()) {
                    return -1;
                } else {
                    return 0;
                }
            }
            //否则以内存进行排序
            else {
                if (ecs1.getMems() > ecs2.getMems()) {
                    return 1;
                } else if (ecs1.getMems() < ecs2.getMems()) {
                    return -1;
                } else {
                    return 0;
                }
            }
        }
    }

    /**
     * @Author: Chy
     * @Description: 自定义的虚拟机比较器
     * @Date: 23:34 2018/3/10
     */
    private static class VmComparator implements Comparator<Map<String, VirtualMachine>> {

        private String target;

        public VmComparator(String target) {
            this.target = target;
        }

        @Override
        public int compare(Map<String, VirtualMachine> map1, Map<String, VirtualMachine> map2) {

            VirtualMachine map1Value = new VirtualMachine();
            VirtualMachine map2Value = new VirtualMachine();
            for (String key : map1.keySet()) {
                map1Value = map1.get(key);
            }
            for (String key : map2.keySet()) {
                map2Value = map2.get(key);
            }

            //若优化目标是CPU，则以CPU核心数进行排序
            if (target.equals("CPU")) {
                if (map1Value.getCPUs() > map2Value.getCPUs()) {
                    return -1;
                } else if (map1Value.getCPUs() < map2Value.getCPUs()) {
                    return 1;
                } else {
                    return 0;
                }
            }
            //否则以内存进行排序
            else {
                if (map1Value.getMems() > map2Value.getMems()) {
                    return -1;
                } else if (map1Value.getMems() < map2Value.getMems()) {
                    return 1;
                } else {
                    return 0;
                }
            }
        }

        public String getTarget() {
            return target;
        }

        public void setTarget(String target) {
            this.target = target;
        }
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 计算资源使用率
     * @Date: 17:34 2018/3/10
     */
    private static double calUseRate(List<ElasticCloudServer> ecsList, int[] totalVmInfo, List<ElasticCloudServer> allEcs) {
        int ecsNum = ecsList.size();  //服务器数量
        int totalEcsCPU = 0;    //服务器总CPU核心数
        int totalEcsMem = 0;   //服务器总内存
        double useRate = 0.0;   //资源使用率

        //计算服务器总资源
        for (ElasticCloudServer ecs : ecsList){
            if (ecs.getName().equals(allEcs.get(0).getName())){
                totalEcsCPU += allEcs.get(0).getCPUs();
                totalEcsMem += allEcs.get(0).getMems();
            }else if (ecs.getName().equals(allEcs.get(1).getName())){
                totalEcsCPU += allEcs.get(1).getCPUs();
                totalEcsMem += allEcs.get(1).getMems();
            }else if (ecs.getName().equals(allEcs.get(2).getName())){
                totalEcsCPU += allEcs.get(2).getCPUs();
                totalEcsMem += allEcs.get(2).getMems();
            }
        }

        useRate =  (totalVmInfo[0] * 1.0 / totalEcsCPU + totalVmInfo[1] * 1.0 / totalEcsMem) / 2;
        return useRate;
    }

    /**
     * @Author: Chy
     * @Param:
     * @Description: 计算预测准确率
     * @Date: 15:00 2018/3/11
     */
    private static double calPredictAccuracyRate(Map<String, VirtualMachine> vmMap, String[] predict, String[] testData) {
        //1.获取测试数据里，各种flavor的数量
        List<String> nameList = new ArrayList<>();
        for (int i = 0; i < testData.length; i++) {
            nameList.add(testData[i].split("\t")[1]);
        }
        Set<String> tempSet = new HashSet<>(nameList);
        Map<String, Integer> realCountMap = new HashMap<>();
        for (String vmName : tempSet) {
            realCountMap.put(vmName, Collections.frequency(nameList, vmName));
        }
        //2.计算预测准确率
        int sumOfRealQuadratic = 0;  //分母里实际结果的平方和
        int sumOfPredictQuadratic = 0;  //分母里预测结果的平方和
        int sumOfDiffQuadratic = 0;  //分子里差的平方和
        for (String predictStr : predict) {
            String vmNameOfPredict = predictStr.split(" ")[0];
            int vmNumsOfPredict = Integer.parseInt(predictStr.split(" ")[1]);  //预测的数量
            int vmNumsOfReal = realCountMap.get(vmNameOfPredict) == null ? 0 : realCountMap.get(vmNameOfPredict); //实际的数量

            sumOfRealQuadratic += vmNumsOfReal * vmNumsOfReal;
            sumOfPredictQuadratic += vmNumsOfPredict * vmNumsOfPredict;
            sumOfDiffQuadratic += (vmNumsOfReal - vmNumsOfPredict) * (vmNumsOfReal - vmNumsOfPredict);
        }

        double numerator = Math.sqrt(sumOfDiffQuadratic * 1.0 / vmMap.size()); //分子
        double denominator = Math.sqrt(sumOfRealQuadratic * 1.0 / vmMap.size()) + Math.sqrt(sumOfPredictQuadratic * 1.0 / vmMap.size()); //分母
        double accuracy = 1 - numerator / denominator;

        return accuracy;
    }
}